package com.mwaysolutions.ocm;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.Calendar;
import java.util.Date;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFactory;

public class OcmUtils {

	public static Value getValue(final Object value, final ValueFactory valueFactory) {
		if (value == null) {
			return null;
		}
	
		if (value instanceof String) {
			return valueFactory.createValue((String) value);
		} else if (value instanceof Integer) {
			return valueFactory.createValue((Integer) value);
		} else if (value instanceof Long) {
			return valueFactory.createValue((Long) value);
		} else if (value instanceof Short) {
			return valueFactory.createValue((Short) value);
		} else if (value instanceof Double) {
			return valueFactory.createValue((Double) value);
		} else if (value instanceof Float) {
			return valueFactory.createValue((Float) value);
		} else if (value instanceof Byte) {
			return valueFactory.createValue((Byte) value);
		} else if (value instanceof Boolean) {
			return valueFactory.createValue((Boolean) value);
		} else if (value instanceof Date) {
			final Calendar cal = Calendar.getInstance();
			cal.setTime((Date) value);
			return valueFactory.createValue(cal);
		} else if (value instanceof Calendar) {
			return valueFactory.createValue((Calendar) value);
		} else if (value instanceof InputStream) {
			return valueFactory.createValue((InputStream) value);
		} else if (value instanceof byte[]) {
			return valueFactory.createValue(new ByteArrayInputStream((byte[]) value));
		} else if (value instanceof Enum) {
			return valueFactory.createValue(((Enum<?>) value).name());
		} else {
			throw new IllegalArgumentException("Unsupported field type: " + value.getClass().getName());
		}
	}

	public static Object getValue(final Property property) throws RepositoryException {
		if (property.getDefinition().isMultiple()) {
			final Value[] values = property.getValues();
			switch (property.getType()) {
			case PropertyType.STRING:
			case PropertyType.PATH:
			case PropertyType.REFERENCE:
			case PropertyType.NAME:
				final String[] stringArr = (String[]) Array.newInstance(String.class, values.length);
				for (int i = 0; i < values.length; i++) {
					stringArr[i] = values[i].getString();
				}
				return stringArr;
			case PropertyType.LONG:
				final long[] longArr = (long[]) Array.newInstance(long.class, values.length);
				for (int i = 0; i < values.length; i++) {
					longArr[i] = values[i].getLong();
				}
				return longArr;
			case PropertyType.DOUBLE:
				final double[] doubleArr = (double[]) Array.newInstance(double.class, values.length);
				for (int i = 0; i < values.length; i++) {
					doubleArr[i] = values[i].getDouble();
				}
				return doubleArr;
			case PropertyType.BOOLEAN:
				final boolean[] booleanArr = (boolean[]) Array.newInstance(boolean.class, values.length);
				for (int i = 0; i < values.length; i++) {
					booleanArr[i] = values[i].getBoolean();
				}
				return booleanArr;
			case PropertyType.BINARY:
				final InputStream[] streamArr = (InputStream[]) Array.newInstance(InputStream.class, values.length);
				for (int i = 0; i < values.length; i++) {
					streamArr[i] = values[i].getStream();
				}
				return streamArr;
			case PropertyType.DATE:
				final Date[] dateArr = (Date[]) Array.newInstance(Calendar.class, values.length);
				for (int i = 0; i < values.length; i++) {
					dateArr[i] = values[i].getDate().getTime();
				}
				return dateArr;
			default:
				throw new IllegalArgumentException("Unsupported Value Type");
			}
		}
	
		final Value value = property.getValue();
		switch (value.getType()) {
		case PropertyType.STRING:
		case PropertyType.PATH:
		case PropertyType.REFERENCE:
		case PropertyType.NAME:
			return value.getString();
		case PropertyType.LONG:
			return value.getLong();
		case PropertyType.DOUBLE:
			return value.getDouble();
		case PropertyType.BOOLEAN:
			return value.getBoolean();
		case PropertyType.BINARY:
			return value.getStream();
		case PropertyType.DATE:
			return value.getDate().getTime();
		default:
			throw new IllegalArgumentException("Unsupported Value Type");
		}
	}

	public static int getJcrPropertyType(final Class<?> clazz) {
		if (String.class.isAssignableFrom(clazz)) {
			return PropertyType.STRING;
		} else if (Long.class.isAssignableFrom(clazz) || long.class.isAssignableFrom(clazz) ||
				Integer.class.isAssignableFrom(clazz) || int.class.isAssignableFrom(clazz) ||
				Short.class.isAssignableFrom(clazz) || short.class.isAssignableFrom(clazz)) {
			return PropertyType.LONG;
		} else if (Double.class.isAssignableFrom(clazz) || double.class.isAssignableFrom(clazz) ||
				Float.class.isAssignableFrom(clazz) || float.class.isAssignableFrom(clazz)) {
			return PropertyType.DOUBLE;
		} else if (Date.class.isAssignableFrom(clazz) || Calendar.class.isAssignableFrom(clazz)) {
			return PropertyType.DATE;
		} else if (byte[].class.isAssignableFrom(clazz) || InputStream.class.isAssignableFrom(clazz)) {
			return PropertyType.BINARY;
		} else if (Boolean.class.isAssignableFrom(clazz) || boolean.class.isAssignableFrom(clazz)) {
			return PropertyType.BOOLEAN;
		} else if (Enum.class.isAssignableFrom(clazz)) {
			return PropertyType.STRING;
		} else {
			throw new IllegalArgumentException("unsupported JCR primitive type: " + clazz);
		}
	}

	public static void removeAllNodes(final Node node, final String name) throws RepositoryException {
		for (final NodeIterator ni = name == null ? node.getNodes() : node.getNodes(name); ni.hasNext(); ) {
			ni.nextNode().remove();
		}
	}

	public static void removeProperty(final Node node, final String name) throws RepositoryException {
		if (node.hasProperty(name)) {
			node.getProperty(name).remove();
		}
	}

}
